<template>
  <div ref="container" class="model-viewer">
    <div v-if="loading" class="loading-overlay">
      <div class="spinner"></div>
      <div class="progress">{{ progress }}%</div>
    </div>
    <div v-if="error" class="error-message">{{ error }}</div>
  </div>
</template>

<script>
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader';
// import STLLoader from 'three-stl-loader';
// import OBJLoader from 'three-obj-loader';

import { markRaw } from 'vue';

export default {
  name: 'ModelViewer',
  props: {
    fileUrl: {
      type: String,
      required: true
    },
    fileType: {
      type: String,
      required: true,
      validator: value => ['stl', 'obj', 'gltf', 'glb'].includes(value)
    },
    backgroundColor: {
      type: String,
      default: '#f0f0f0'
    },
    modelColor: {
      type: String,
      default: '#4a8bfc'
    }
  },
  data() {
    return {
      scene: markRaw(new THREE.Scene()),
      camera: markRaw(new THREE.PerspectiveCamera()),
      renderer: null,
      controls: null,
      model: null,
      loading: false,
      progress: 0,
      error: null,
      animationId: null
    };
  },
  mounted() {
    this.initScene();
    this.loadModel();
    window.addEventListener('resize', this.handleResize);
  },
  beforeUnmount() {
    this.cleanup();
  },
  methods: {
    initScene() {
      // 初始化场景
      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(this.backgroundColor);

      // 初始化相机
      const container = this.$refs.container;
      const width = container.clientWidth;
      const height = container.clientHeight;
      
      this.camera = new THREE.PerspectiveCamera(75, width / height, 0.1, 1000);
      this.camera.position.z = 5;

      // 初始化渲染器
      this.renderer = markRaw(new THREE.WebGLRenderer());
      // this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setPixelRatio(window.devicePixelRatio);
      this.renderer.setSize(width, height);
      container.appendChild(this.renderer.domElement);

      // 添加光源
      this.addLights();

      // 添加控制器
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);
      this.controls.enableDamping = true;
      this.controls.dampingFactor = 0.25;

      // 开始渲染循环
      this.animate();
    },
    addLights() {
      const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
      this.scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
      directionalLight.position.set(1, 1, 1);
      this.scene.add(directionalLight);
    },
    loadModel() {
      this.loading = true;
      this.error = null;
      console.log(this.fileType)
      switch (this.fileType) {
        case 'stl':
          this.loadSTL();
          break;
        case 'obj':
          this.loadOBJ();
          break;
        case 'gltf':
        case 'glb':
          this.loadGLTF();
          break;
        default:
          this.error = `不支持的文件类型: ${this.fileType}`;
          this.loading = false;
      }
    },
    loadSTL() {
      const manager = new THREE.LoadingManager();
      const loader = new STLLoader(manager);
      loader.load(
        this.fileUrl,
        geometry => {
          this.setupModel(geometry);
        },
        xhr => {
          this.progress = Math.round((xhr.loaded / xhr.total) * 100);
        },
        error => {
          this.handleError(error);
        }
      );
    },
    loadOBJ() {
      const loader = new OBJLoader(THREE);
      loader.load(
        this.fileUrl,
        object => {
          this.model = object;
          this.scene.add(object);
          this.centerModel();
          this.loading = false;
        },
        xhr => {
          this.progress = Math.round((xhr.loaded / xhr.total) * 100);
        },
        error => {
          this.handleError(error);
        }
      );
    },
    loadGLTF() {
      // 需要安装 GLTFLoader
      import('three/examples/jsm/loaders/GLTFLoader').then(({ GLTFLoader }) => {
        
        // const loader = new GLTFLoader();
            // 创建 LoadingManager
    const manager = new THREE.LoadingManager();
    
    // 初始化加载器并传入 manager
    const loader = new GLTFLoader(manager);
        loader.load(
          this.fileUrl,
          gltf => {
            this.model = gltf.scene;
            this.scene.add(gltf.scene);
            this.centerModel();
            this.loading = false;
          },
          xhr => {
            this.progress = Math.round((xhr.loaded / xhr.total) * 100);
          },
          error => {
            this.handleError(error);
          }
        );
      });
    },
    setupModel(geometry) {
      // 计算法线
      geometry.computeVertexNormals();

      // 创建材质
      const material = new THREE.MeshPhongMaterial({
        color: new THREE.Color(this.modelColor),
        specular: 0x111111,
        shininess: 50
      });

      // 创建网格
      this.model = new THREE.Mesh(geometry, material);
      this.scene.add(this.model);

      // 居中模型
      this.centerModel();

      this.loading = false;
    },
    centerModel() {
      if (!this.model) return;

      const box = new THREE.Box3().setFromObject(this.model);
      const center = new THREE.Vector3();
      box.getCenter(center);

      this.model.position.sub(center);
      this.camera.lookAt(center);

      // 调整相机距离
      const size = box.getSize(new THREE.Vector3());
      const maxDim = Math.max(size.x, size.y, size.z);
      const fov = this.camera.fov * (Math.PI / 180);
      let cameraZ = Math.abs(maxDim / 2 * Math.tan(fov * 2));
      cameraZ *= 1.5;

      this.camera.position.z = cameraZ;
      this.controls.target.copy(center);
      this.controls.update();
    },
    // animate() {
    //   this.animationId = requestAnimationFrame(this.animate);
    //   this.controls.update();
    //   this.renderer.render(this.scene, this.camera);
    // },
    animate() {
  if (!this.renderer || !this.scene || !this.camera) return;
  
  this.animationId = requestAnimationFrame(this.animate);
  if (this.controls) {
    this.controls.update();
  }
  this.renderer.render(this.scene, this.camera);
  

},
    handleResize() {
      const container = this.$refs.container;
      const width = container.clientWidth;
      const height = container.clientHeight;
      
      this.camera.aspect = width / height;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(width, height);
    },
    handleError(error) {
      console.error('加载模型错误:', error);
      this.error = '加载模型失败';
      this.loading = false;
    },
    cleanup() {
      window.removeEventListener('resize', this.handleResize);
      cancelAnimationFrame(this.animationId);
      
      if (this.controls) this.controls.dispose();
      if (this.renderer) {
        this.renderer.dispose();
        const container = this.$refs.container;
        if (container && this.renderer.domElement) {
          container.removeChild(this.renderer.domElement);
        }
      }
    }
  },
  watch: {
    fileUrl() {
      if (this.model) {
        this.scene.remove(this.model);
        this.model = null;
      }
      this.loadModel();
    },
    modelColor(newColor) {
      if (this.model && this.model.material) {
        this.model.material.color.set(newColor);
      }
    }
  }
};
</script>

<style scoped>
.model-viewer {
  width: 100%;
  height: 500px;
  position: relative;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: rgba(0, 0, 0, 0.2);
  z-index: 10;
}

.spinner {
  width: 40px;
  height: 40px;
  border: 4px solid rgba(255, 255, 255, 0.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
}

.progress {
  margin-top: 10px;
  color: white;
}

.error-message {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  padding: 10px;
  background-color: #ff6b6b;
  color: white;
  text-align: center;
  z-index: 10;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}
</style>